﻿using System;
using System.Collections.Generic;
using Bettzueche.RLLibrary.Types;
using System.Windows;

namespace AMEE1
{
    /// <summary>
    /// Konjunktive Verknüpfung der Zustandsdimensionen. Gibt die zu einem Zustand gehörigen Merkmalsnummern.
    /// </summary>
    class DimensionCombination : IFeatures<State>
    {
        //MapConfiguration _mapConf;
        int _dirCount;
        int _anzAbschnitte;
        int _anzRichtungen;

        /// <summary>
        /// Gibt oder setzt die Segmentierung des Unebenheitsmaßes.
        /// </summary>
        /// <value>
        /// The bumpiness tiling.
        /// </value>
        public MultiTiling BumpinessTiling { get; set; }

        /// <summary>
        /// Gibt oder setzt die Segmentierung der Zielentfernungen.
        /// </summary>
        /// <value>
        /// The distance tiling.
        /// </value>
        public Entfernungen DistanceTiling { get; set; }

        /// <summary>
        /// Gibt oder setzt die Funktion, die aus dem Vektor agent->ziel die Zielrichtung ermittelt.
        /// </summary>
        /// <value>
        /// The direction feature function.
        /// </value>
        internal Func<Vector, int> DirectionFeatureFunction { get; set; }

        internal Func<Vector, int> DistanceFeatureFunction { get; set; }

        // DirectionTiling

        /// <summary>
        /// Initializes a new instance of the <see cref="DimensionCombination"/> class.
        /// </summary>
        /// <param name="mapConfig">The map configuration.</param>
        /// <exception cref="System.NullReferenceException">if mapConfig is null</exception>
        public DimensionCombination(MapConfiguration mapConfig) {
            BumpinessTiling = mapConfig.BumpinessTiling;
            DistanceTiling = mapConfig.EntfernungsTiling;
            DirectionFeatureFunction = mapConfig.GetZielrichtung;
            DistanceFeatureFunction = mapConfig.GetZielentfernung;
            _dirCount = mapConfig.Zielrichtungen;
            _anzAbschnitte = mapConfig.Abschnitte;
            _anzRichtungen = mapConfig.Zielrichtungen;
        }

        #region IFeatures<State> Members

        /// <summary>
        /// Gibt die Merkmalsmenge(bzw. die Merkmalsnummern), die aus den Zustandseigenschaften mittels 
        /// konjunktiver Verknüpfung der Unebenheiten, Richtung und Distanz ermittelt werden.
        /// </summary>
        /// <param name="state">Der Zustand</param>
        /// <returns>IDs (Indizes) der Merkmale, die zu den übergebenen Zustandsdaten gehören.</returns>
        public virtual int[] GetFeatureSet(State state) {
            // BumpinessTiling kann ein MultiTiling sein. Anzahl der Merkmale dann 0 bis #Tilings
            int[] retVal = new int[BumpinessTiling.TilingCount];

            int idxOffset = 0;
            int outOfBoundsCounter = 0;

            // Richtung
            int dirFtr = state.Zielrichtung;
            for (int i = 0; i < retVal.Length; i++) {
                retVal[i] = dirFtr;
            }
            idxOffset = _dirCount;

            // Distanz
            int distFtr = DistanceTiling.GetPartitionNumber(state.MinSchritte);
            if (distFtr == Tiling.OutOfBounds) {
                return new int[0];
            }
            for (int i = 0; i < retVal.Length; i++) {
                distFtr *= idxOffset;
                retVal[i] += distFtr;
            }
            idxOffset *= DistanceTiling.Partitions;

            // Abschnittsunebenheiten
            int[] uFtrArr = null;
            for (int uIdx = 0; uIdx < state.Unebenheiten.Length; uIdx++) {
                uFtrArr = BumpinessTiling.GetFeatureNumbers(state.Unebenheiten[uIdx]);
                for (int i = 0; i < retVal.Length; i++) { //retVal.Lengt == uFtrArr.Length
                    if (retVal[i] == Tiling.OutOfBounds) {
                        continue;
                    }
                    if (uFtrArr[i] == Tiling.OutOfBounds) {
                        retVal[i] = Tiling.OutOfBounds;
                        outOfBoundsCounter++;
                        continue;
                    }
                    uFtrArr[i] *= idxOffset;
                    retVal[i] += uFtrArr[i];
                } // end for all uFtrArr
                idxOffset *= BumpinessTiling.TileCount;
            } // end for all unebenheiten

            // Reduziere retVal, fall OutOfBounds enthält
            if (outOfBoundsCounter > 0) {
                List<int> newRetVal = new List<int>(retVal.Length);
                for (int i = 0; i < retVal.Length; i++) {
                    if (retVal[i] != Tiling.OutOfBounds) {
                        newRetVal.Add(retVal[i]);
                    }
                }
                return newRetVal.ToArray();
            }
            else {
                return retVal;
            }
        }

        /// <summary>
        /// Optional! Nicht implementiert! Gets the state from features.
        /// </summary>
        /// <param name="features">The features.</param>
        /// <returns></returns>
        public virtual State GetStateFromFeatures(int[] features) {
            throw new NotImplementedException();
        }

        #endregion

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString() {
            return "Konjunktion |u="+BumpinessTiling.TileCount+"|^|Ab="+_anzAbschnitte+"| * |Dist="+DistanceTiling.Partitions+"| * |Dir="+_anzRichtungen+"|";
        }
    }
}
